﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Xml;

namespace Weazel.Willows.Model
{
	/// <summary>
	/// A type registry is used to organize named type. The registry
	/// maps names onto actual types.
	/// </summary>
	public class TypeRegistry
	{
		protected Dictionary<string, BasicType> registry =
			new Dictionary<string, BasicType>();

		public TypeRegistry()
		{
			registry.Add("unsigned", new UnsignedType(this));
			registry.Add("signed", new SignedType(this));
			registry.Add("boolean", new Type(this, registry["unsigned"], 1, "boolean"));
		}

		public BasicType this[string name]
		{
			get 
			{
				if (!registry.ContainsKey(name))
					throw new UnknownTypeException(name);

				return registry[name]; 
			}	
		}

		public void Read(ref List<ModelError> errors, XmlNodeList typeEntries)
		{
			foreach (XmlNode node in typeEntries)
			{
				// process only "type" tags who are children of "model".
				if (node.ParentNode.Name != "Model")
					continue;

				System.Diagnostics.Debug.Assert(node is XmlElement);
				XmlElement description = node as XmlElement;

				if (description.HasChildNodes)
				{
					// if the type has nodes it must be a compound type
					CompoundType compoundType = new CompoundType(this);
					compoundType.Read(description);

					if (registry.ContainsKey(compoundType.Name))
					{
						string message =
							string.Format("Duplicate definition of Type '{0}'.", compoundType.Name);

						errors.Add(new ModelError(message));
					}
					else
					{
						registry.Add(compoundType.Name, compoundType);
					}
				}
				else
				{
					if (description.HasAttribute("width"))
					{
						// must be a type
						Weazel.Willows.Model.Type type = new Weazel.Willows.Model.Type(this);
						type.Read(description);

						if (registry.ContainsKey(type.Name))
						{
							string message =
								string.Format("Duplicate definition of Type '{0}'.", type.Name);

							errors.Add(new ModelError(message));
						}
						else
						{
							registry.Add(type.Name, type);
						}
					}
					else
					{
						// must be an alias
						AliasType alias = new AliasType(this);
						alias.Read(description);
						if (registry.ContainsKey(alias.Name))
						{
							string message =
								string.Format("Duplicate definition of Type '{0}'.", alias.Name);

							errors.Add(new ModelError(message));
						}
						else
						{
							registry.Add(alias.Name, alias);
						}
					}
				}
			}

			resolve();
		}

		private void resolve()
		{
			foreach (BasicType type in registry.Values)
				type.Resolve();
		}

		public override string ToString()
		{
			StringBuilder str = new StringBuilder();
			foreach (string key in registry.Keys)
				str.Append(String.Format("{0}:\t {1} \n", key, registry[key]));

			return str.ToString();
		}
	}
}
